package debtbundler.presentation.debtviewer;

import java.awt.Dimension;
import java.text.NumberFormat;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;

import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;

import debtbundler.domain.bll.Utils;
import debtbundler.domain.entity.Debt;
import debtbundler.domain.entity.Person;
import debtbundler.presentation.basecomponents.DomainViewComponent;
import debtbundler.presentation.layout.MultiSizeGridLayout;
import debtbundler.presentation.utils.IconConstants;
import debtbundler.presentation.utils.UIUtils;

/**
 * Presents debts in a balanceSheetGrid. At most debt is allowed between any two persons.
 * 
 * @author Anssi
 *
 */
public class DebtGrid extends JComponent implements DomainViewComponent<Collection<Debt>> {
	private static final long serialVersionUID = 1L;
	
	public static enum Orientation {CREDITORS_ON_COLS, CREDITORS_ON_ROWS}
	
	protected Set<Person> debtors = new LinkedHashSet<Person>();
	protected Set<Person> creditors = new LinkedHashSet<Person>();
	protected Collection<Debt> debts;
	
	private Orientation orientation; 
	protected NumberFormat currencyFormat = NumberFormat.getCurrencyInstance();

	public DebtGrid(Collection<Debt> debts) {
		
		this.debts = debts;
		
		if (debts == null || debts.size() == 0) {
			this.add( new JLabel("Ei velkoja") );
			return;
		}
		
		for (Debt debt : debts) {
			debtors.add(debt.getDebtor());
			creditors.add(debt.getCreditor());
			
			if (creditors.contains(debt.getDebtor()) || debtors.contains(debt.getCreditor())) {
				throw new IllegalArgumentException("Person not allowed to be both debtor and creditor");
			}
		}
		
		if (debtors.size()*creditors.size() < debts.size()) {
			throw new IllegalArgumentException("Max one debt between between any two persons");
		}
		
		setOrientation(Orientation.CREDITORS_ON_COLS);
	}

	private void layCreditorsOnCols() {
		this.setLayout( new MultiSizeGridLayout(debtors.size()+2, creditors.size()+2, 0, 0));
		
		// Upper left corner
		this.add(new JPanel());
		
		// Creditors on columns
		for (Person creditor : creditors) {
			this.add(makePersonBox(IconConstants.CREDITOR_16, creditor.getName()));
		}
		
		// Upper right corner
		this.add(new JPanel());
		
		// Rows: first debtor name, then her debt to each creditor (if has), finally total debts of debtor
		// TODO this is quite an inefficient way to build the grid
		for (Person debtor : debtors) {
			
			this.add(makePersonBox(IconConstants.DEBTOR_16, debtor.getName()));
			
			for (Person creditor : creditors) {
				boolean hasDebt = false;
				Iterator<Debt> iter = debts.iterator();
				while (!hasDebt && iter.hasNext()) {
					Debt debt = iter.next();
					if ( debtor.equals(debt.getDebtor()) && creditor.equals(debt.getCreditor()) ) {
						this.add( makeDebtBox(debt.getValue()) );
						hasDebt = true;
					}
				}
				
				if (!hasDebt) {
					JPanel emptyPanel = new JPanel();
					setBorder(emptyPanel);
					this.add(emptyPanel);
				}
			}
			
			// Total debt of debtor
			this.add(makeTotalBox( Utils.sum(debtor.getDebts()) ));
		}
		
		// Lower left corner
		this.add(new JPanel());
		
		// Total credit of each creditor
		for (Person creditor : creditors) {
			this.add(makeTotalBox( Utils.sum(creditor.getDebts()) ));
		}
		
		// Lower right corner
		this.add(new JPanel());
	}

	private void layCreditorsOnRows() {
		this.setLayout( new MultiSizeGridLayout(creditors.size()+2, debtors.size()+2, 0, 0));
		
		// Upper left corner
		this.add(new JPanel());
		
		// Debtors on columns
		for (Person debtor : debtors) {
			this.add(makePersonBox(IconConstants.DEBTOR_16, debtor.getName()));
		}
		
		// Upper right corner
		this.add(new JPanel());
		
		// Rows: first creditor, then her debt from each debtor (if has), finally total credit of creditor
		// TODO this is quite an inefficient way to build the grid
		for (Person creditor : creditors) {
			
			// Creditor name
			this.add(makePersonBox(IconConstants.CREDITOR_16, creditor.getName()));
			
			// Debts to creditor
			for (Person debtor : debtors) {
				boolean hasDebt = false;
				Iterator<Debt> iter = debts.iterator();
				while (!hasDebt && iter.hasNext()) {
					Debt debt = iter.next();
					if ( creditor.equals(debt.getCreditor()) && debtor.equals(debt.getDebtor()) ) {
						this.add( makeDebtBox(debt.getValue()) );
						hasDebt = true;
					}
				}
				
				if (!hasDebt) {
					JPanel emptyPanel = new JPanel();
					setBorder(emptyPanel);
					this.add(emptyPanel);
				}
			}
			
			// Total credit of current creditor
			this.add(makeTotalBox( Utils.sum(creditor.getDebts()) ));
		}
		
		// Lower left corner
		this.add(new JPanel());
		
		// Total debt of each debtor
		for (Person debtor : debtors) {
			this.add(makeTotalBox( Utils.sum(debtor.getDebts()) ));
		}
		
		// Lower right corner
		this.add(new JPanel());
	}	
		
	private Box makePersonBox(String iconImagePath, String name) {
		
		JTextField nameField = new JTextField(name);
		nameField.setEditable(false);
		nameField.setBorder(null);
		JLabel titleLabel = new JLabel(UIUtils.getImageIcon(iconImagePath));
		titleLabel.setLabelFor(nameField);
		
		Box innerBox = Box.createHorizontalBox();
		innerBox.add(titleLabel);
		innerBox.add(Box.createRigidArea( new Dimension(3, 0) ));
		innerBox.add(nameField);
		innerBox.setBorder(BorderFactory.createEmptyBorder(6, 6, 6, 6));
		
		Box outerBox = Box.createVerticalBox();
		outerBox.add(innerBox);
		outerBox.add(Box.createVerticalGlue());
		setBorder(outerBox);	
		
		return outerBox;
	}
	
	private Box makeDebtBox(double value) {
		JTextField valueField = new JTextField(currencyFormat.format(value));
		valueField.setEditable(false);
		valueField.setBorder(null);
		
		Box innerBox = Box.createHorizontalBox();
		innerBox.add(valueField);
		innerBox.setBorder(BorderFactory.createEmptyBorder(6, 6, 6, 6));
		
		Box outerBox = Box.createVerticalBox();
		outerBox.add(innerBox);
		outerBox.add(Box.createVerticalGlue());
		setBorder(outerBox);	
		
		return outerBox;
	}
	
	private Box makeTotalBox(double value) {
		JTextField valueField = new JTextField(currencyFormat.format(value));
		valueField.setEditable(false);
		valueField.setBorder(null);
		
		JLabel titleLabel = new JLabel(UIUtils.getImageIcon(IconConstants.TOTAL_DEBT_16));
		titleLabel.setLabelFor(valueField);
		
		Box innerBox = Box.createHorizontalBox();
		innerBox.add(titleLabel);
		innerBox.add(Box.createRigidArea( new Dimension(3, 0) ));
		innerBox.add(valueField);
		innerBox.setBorder(BorderFactory.createEmptyBorder(6, 6, 6, 6));
		
		Box outerBox = Box.createVerticalBox();
		outerBox.add(innerBox);
		outerBox.add(Box.createVerticalGlue());
		setBorder(outerBox);	
		
		return outerBox;
	}
	
	private void setBorder(JComponent comp) {
		comp.setBorder( BorderFactory.createEtchedBorder());
	}
	
	public void setOrientation(Orientation orientation) {
		this.removeAll();
		this.orientation = orientation;
		if (this.orientation == Orientation.CREDITORS_ON_COLS) {
			layCreditorsOnCols();
		} else if (this.orientation == Orientation.CREDITORS_ON_ROWS) {
			layCreditorsOnRows();
		}
		this.validate();
	}
	
	public Orientation getOrientation() {
		return this.orientation;
	}

	@Override
	public Collection<Debt> getDomainComponent() {
		return this.debts;
	}

	@Override
	public void refreshDomainFields() {
		setOrientation(this.orientation);
	}
}
